/* -*- Mode: C; tab-width: 4;  c-basic-offset: 4                -*- */
/*
 * viewer.c : A collection of widgets to show and manipulate files of a host.
 * Copyright (C) 2009 Lingtao Pan
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, 
 * Boston, MA 02111-1307, USA.
 */


#include "common.h"


const char *stock_dir = "gtk-directory";
const char *stock_file = "gtk-file";
const char *stock_up = "gtk-go-up";


static void free_viewer_files      (Viewer *viewer);
static void do_list_files          (Viewer *viewer);
static void on_viewer_filter_entry_activate (GtkEntry *entry, Viewer *viewer);
static void on_upbtn_clicked       (GtkButton *button, Viewer *viewer);
static void on_backbtn_clicked (GtkButton *button, Viewer *viewer);
static void on_fwdbtn_clicked (GtkButton *button, Viewer *viewer);

static void on_viewer_directory_changed (Viewer *viewer);

static  GtkListStore *
create_model (void)
{
    GtkListStore *store;
  
    store = gtk_list_store_new (COLUMNS, G_TYPE_STRING, G_TYPE_STRING, 
                                G_TYPE_STRING, G_TYPE_STRING,
                                G_TYPE_STRING, G_TYPE_STRING,
                                G_TYPE_STRING,
                                G_TYPE_POINTER);

    return store;
}

static void column_clicked (GtkTreeViewColumn *treeviewcolumn, Viewer *viewer)
{
    GtkTreeSortable *sortable;
    GtkSortType order;
    int column_id;
    

    order = gtk_tree_view_column_get_sort_order (treeviewcolumn);
    column_id = (int) g_object_get_data (G_OBJECT (treeviewcolumn), 
                                         "sort-column");
    
    sortable = GTK_TREE_SORTABLE (viewer->store);   

    gtk_tree_sortable_set_sort_column_id (sortable, column_id, order);

    order = (order == GTK_SORT_ASCENDING) ? 
        GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;

    gtk_tree_view_column_set_sort_order (treeviewcolumn, order);
}

static void
setup_tree_view (GtkWidget *treeview, Viewer *viewer)
{
    GtkCellRenderer *renderer;
    GtkTreeViewColumn *column;
    GtkTreeSelection *selection;

    renderer = gtk_cell_renderer_pixbuf_new ();
    column = gtk_tree_view_column_new_with_attributes ("", renderer, 
                                         "stock_id", COLUMN_STOCK, NULL);
    gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
    gtk_tree_view_column_set_clickable (column, TRUE);
    g_signal_connect (column, "clicked", G_CALLBACK (column_clicked), viewer);
    gtk_tree_view_column_set_sort_indicator (column, TRUE);
    gtk_tree_view_column_set_sort_order (column, GTK_SORT_ASCENDING);
    g_object_set_data (G_OBJECT (column), "sort-column", 
                       (gpointer) COLUMN_STOCK);

    viewer->first_column = column;
  

    renderer = gtk_cell_renderer_text_new ();
    column = gtk_tree_view_column_new_with_attributes (_("Filename"), 
                               renderer, "text", COLUMN_FILENAME, NULL);
    gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);

    gtk_tree_view_column_set_clickable (column, TRUE);
    g_signal_connect (column, "clicked", G_CALLBACK (column_clicked), viewer);
    gtk_tree_view_column_set_sort_indicator (column, TRUE);
    gtk_tree_view_column_set_sort_order (column, GTK_SORT_ASCENDING);
    g_object_set_data (G_OBJECT (column), "sort-column", 
                       (gpointer) COLUMN_FILENAME);


    renderer = gtk_cell_renderer_text_new ();
    column = gtk_tree_view_column_new_with_attributes (_("Size"), 
                               renderer, "text", COLUMN_FILESIZE, NULL);
    g_object_set (renderer, "xalign", 1.0, NULL);
    gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);

    renderer = gtk_cell_renderer_text_new ();
    column = gtk_tree_view_column_new_with_attributes (_("User"), 
                               renderer, "text", COLUMN_USER, NULL);
    gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);

    renderer = gtk_cell_renderer_text_new ();
    column = gtk_tree_view_column_new_with_attributes (_("Group"), 
                               renderer, "text", COLUMN_GROUP, NULL);
    gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);

    renderer = gtk_cell_renderer_text_new ();
    column = gtk_tree_view_column_new_with_attributes (_("Date"), 
                               renderer, "text", COLUMN_DATE, NULL);

    gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);

    renderer = gtk_cell_renderer_text_new ();
    column = gtk_tree_view_column_new_with_attributes (_("Attribs"), 
                               renderer, "text", COLUMN_ATTRIBS, NULL);
    gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);


    gtk_tree_view_set_search_column (GTK_TREE_VIEW (treeview), 
                                     COLUMN_FILENAME);

    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
    gtk_tree_selection_set_mode (selection, 
                                 GTK_SELECTION_MULTIPLE);

    gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE);
}

gint sort_function (GtkTreeModel *model, GtkTreeIter *a,
                    GtkTreeIter *b, gpointer data)
{
    GtkSortType order;
    gint sort_column_id;
    char *stocka, *stockb;
    gftp_file *fle1, *fle2;

    gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model),
                                          &sort_column_id, &order);

    gtk_tree_model_get (model, a, COLUMN_STOCK, &stocka, 
                        COLUMN_FILEENTRY, &fle1, -1);
    gtk_tree_model_get (model, b, COLUMN_STOCK, &stockb, 
                        COLUMN_FILEENTRY, &fle2, -1);  


    switch (sort_column_id) {
    case COLUMN_FILENAME:
        if (S_ISDIR (fle1->st_mode) && !S_ISDIR(fle2->st_mode)) {
            if (order == GTK_SORT_ASCENDING)
                return -1;
            else
                return 1;
        }

        if (!S_ISDIR (fle1->st_mode) && S_ISDIR(fle2->st_mode)) {
            if (order == GTK_SORT_ASCENDING)
                return 1;
            else
                return -1;
        }

        return (strcmp (fle1->file, fle2->file));

        break;
    case COLUMN_STOCK:
        return (strcmp (stocka, stockb));
        break;
    default:
        fprintf (stderr, "sort function get wrong 'sort_culumn_id'\n");
        return 0;
    }

    return 0;
}

static gboolean gftp_viewer_filter (GtkTreeModel *model,
                                    GtkTreeIter *iter,
                                    Viewer *viewer)
{
  gchar *filename;
  gboolean visible = FALSE, show_hidden;

  gtk_tree_model_get (model, iter, COLUMN_FILENAME, &filename, -1);
  if (filename) {
      show_hidden = gftp_config_get_boolean ("General", "show_hidden_files");

      if (viewer->filter_str != NULL) {
          if (g_pattern_match_string (viewer->pattern_spec, filename))
              visible = TRUE;
      } else {
          visible = TRUE;
      }

      if (!show_hidden) {
          if (filename[0] == '.')
              visible = FALSE;
      }

  }
  g_free (filename);

  return visible;
}

static void add_button (GtkWidget **widget, GtkBox *box, const gchar *stock)
{
    GtkWidget *image;

    image = gtk_image_new_from_stock (stock, GTK_ICON_SIZE_SMALL_TOOLBAR);
    *widget = gtk_button_new ();
    gtk_button_set_image (GTK_BUTTON (*widget), image);
    gtk_button_set_relief (GTK_BUTTON (*widget), GTK_RELIEF_NONE);
    gtk_box_pack_start (box, *widget, FALSE, FALSE, 1);  
}

Viewer*
gftp_viewer_new ()
{
  Viewer *viewer;
  GtkWidget *sw;
  GtkTreeSortable *sortable;
  GtkWidget *hbox, *label;

  viewer = (Viewer *) calloc (sizeof (Viewer), 1);

  viewer->frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (viewer->frame), GTK_SHADOW_OUT);

  viewer->vbox = gtk_vbox_new (FALSE, 1);
  gtk_container_add (GTK_CONTAINER (viewer->frame), viewer->vbox);


  /* -------- toolbar --------*/
  hbox = gtk_hbox_new (FALSE, 1);
  gtk_box_pack_start (GTK_BOX(viewer->vbox), hbox, FALSE, FALSE, 0);

  add_button (&viewer->upbtn,   GTK_BOX (hbox), GTK_STOCK_GO_UP);
  add_button (&viewer->backbtn, GTK_BOX (hbox), GTK_STOCK_GO_BACK);
  add_button (&viewer->fwdbtn,  GTK_BOX (hbox), GTK_STOCK_GO_FORWARD);
  add_button (&viewer->rfrbtn,  GTK_BOX (hbox), GTK_STOCK_REFRESH);
  /* add_button (&viewer->stopbtn, GTK_BOX (hbox), GTK_STOCK_STOP); */

  /*  change the buttons' sensetivity states */
  on_viewer_init (viewer);
  
  label = gtk_label_new (_("Filter:"));
  gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
  gtk_box_pack_start (GTK_BOX(hbox), label, TRUE, TRUE, 0);
  viewer->filter_entry = gtk_entry_new ();  
  gtk_box_pack_start (GTK_BOX(hbox), viewer->filter_entry, FALSE, FALSE, 0);
  gtk_widget_set_tooltip_text (viewer->filter_entry, _("Glob-style pattern "
                   "matching - matches file names against patterns "
                   "containing '*' (wildcard) and '?'"));

  /* combo entry */
  viewer->combo = gtk_combo_box_entry_new_text ();
  gtk_box_pack_start (GTK_BOX(viewer->vbox), viewer->combo, FALSE, FALSE, 0);


  /* treeview */
  viewer->treeview = gtk_tree_view_new ();
  
  sw = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
                                  GTK_POLICY_AUTOMATIC,
                                  GTK_POLICY_AUTOMATIC);
  gtk_box_pack_start (GTK_BOX (viewer->vbox), sw, TRUE, TRUE, 0);
  gtk_container_add (GTK_CONTAINER (sw), viewer->treeview);

  setup_tree_view (viewer->treeview, viewer);
  viewer->store = create_model ();
  viewer->filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (viewer->store),
                                              NULL);
  
  gtk_tree_view_set_model (GTK_TREE_VIEW (viewer->treeview),
                           GTK_TREE_MODEL (viewer->filter));
  gtk_tree_model_filter_set_visible_func (
                GTK_TREE_MODEL_FILTER (viewer->filter),
                (GtkTreeModelFilterVisibleFunc) gftp_viewer_filter, 
                viewer, NULL);

  g_object_unref (viewer->store);
  g_object_unref (viewer->filter);

  /* sort */
  sortable = GTK_TREE_SORTABLE (viewer->store);
  gtk_tree_sortable_set_sort_func (sortable, COLUMN_STOCK, sort_function,
                                   NULL, NULL);
  gtk_tree_sortable_set_sort_func (sortable, COLUMN_FILENAME, sort_function,
                                   NULL, NULL);

  /* --------  callbacks -------- */
  /* treeview */
  g_signal_connect (viewer->treeview, "row-activated",
                    G_CALLBACK(viewer_row_activated_cb), (gpointer) viewer);
  g_signal_connect (viewer->treeview, "popup-menu",
                          G_CALLBACK (viewer_popup_menu_cb), viewer);




  /* combo related */
  g_signal_connect (gtk_bin_get_child(GTK_BIN (viewer->combo)), 
                    "activate", G_CALLBACK (viewer_directory_edited_cb), 
                    (gpointer) viewer);

  viewer->ccc_id = g_signal_connect (viewer->combo, "changed", 
                    G_CALLBACK (viewer_combo_changed_cb), (gpointer) viewer); 



  /* filter */
  g_signal_connect (viewer->filter_entry, "activate", 
                    G_CALLBACK (on_viewer_filter_entry_activate), 
                    (gpointer) viewer);

  /* navigation */
  g_signal_connect (viewer->upbtn, "clicked",
                    G_CALLBACK (on_upbtn_clicked), (gpointer) viewer);
  g_signal_connect_swapped (viewer->rfrbtn, "clicked",
                    G_CALLBACK (refresh_viewer), (gpointer) viewer);
  g_signal_connect (viewer->backbtn, "clicked",
                    G_CALLBACK (on_backbtn_clicked), (gpointer) viewer);
  g_signal_connect (viewer->fwdbtn, "clicked",
                    G_CALLBACK (on_fwdbtn_clicked), (gpointer) viewer);

  return viewer;
}


void
gftp_viewer_clear (Viewer *viewer)
{
    GList *iter;
    GtkTreeModel* model;

    for (iter=viewer->history; iter; iter=iter->next) {
        g_free (iter->data);
    }
    g_list_free (viewer->history);
    viewer->history = NULL;
    viewer->his_pos = NULL;
    viewer->his_len = 0;

    if (viewer->pattern_spec)  {
        g_pattern_spec_free (viewer->pattern_spec);
        viewer->pattern_spec = NULL;
    }

    if (viewer->filter_str) {
        g_free (viewer->filter_str);
        viewer->filter_str = NULL;
    }

    if (viewer->request) {
        gftp_request_destroy (viewer->request, TRUE);
        viewer->request = NULL;
    }

    model = gtk_combo_box_get_model (GTK_COMBO_BOX (viewer->combo));
    gtk_list_store_clear (GTK_LIST_STORE (model));
    gftp_viewer_combo_set_text_no_emission (viewer, "");
    
    gtk_list_store_clear (viewer->store);

    gtk_widget_set_sensitive (viewer->upbtn, FALSE);
    gtk_widget_set_sensitive (viewer->backbtn, FALSE);
    gtk_widget_set_sensitive (viewer->fwdbtn, FALSE);
    gtk_widget_set_sensitive (viewer->rfrbtn, FALSE);    
}



void*
gftp_viewer_get_file_list (Viewer *viewer)
{
  do_list_files (viewer);

  return NULL;
}

void 
gftp_viewer_add_directory_history (Viewer *viewer, gchar *dir, int pos)
{
    g_return_if_fail (pos >= 0);
    HistoryEntry *entry;

    gtk_combo_box_prepend_text (GTK_COMBO_BOX (viewer->combo), dir);

    /* reset the position */
    viewer->his_pos = NULL;

    if (viewer->history != NULL) {
        entry = (HistoryEntry *) viewer->history->data;
        if (g_strcmp0 (entry->path, dir) == 0) {
            entry->pos = pos;
            return;
        }
    }

    entry = g_malloc0 (sizeof (HistoryEntry));
    entry->path = g_strdup (dir);
    entry->pos = pos;
    viewer->history = g_list_prepend (viewer->history, entry);
    
    if (viewer->his_len > MAX_HIS_LEN) {
        GList *last = g_list_last (viewer->history);
        g_free (last->data);
        viewer->history = g_list_delete_link (viewer->history, last);
    } else {
        viewer->his_len++;
    }
   
}

void
gftp_viewer_combo_set_text (Viewer *viewer, gchar *text)
{
    gtk_entry_set_text (
        GTK_ENTRY(gtk_bin_get_child(GTK_BIN (viewer->combo))),
        text);
    

}

void
gftp_viewer_combo_set_text_no_emission (Viewer *viewer, gchar *text)
{
    g_signal_handler_block (viewer->combo, viewer->ccc_id);
    gtk_entry_set_text ( GTK_ENTRY(gtk_bin_get_child
                                  (GTK_BIN (viewer->combo))),
                         text );
    g_signal_handler_unblock (viewer->combo, viewer->ccc_id);
}


void
gftp_viewer_copy_filename (Viewer *viewer)
{
    GtkTreeSelection *selection;
    GtkTreeModel *model;
    GList *slist;
    GtkTreePath *path;
    GtkTreeIter iter;
    gchar *fname;
    GtkClipboard *clipboard;
      
    if (viewer == NULL) {
        g_debug ("gftp_viewer_copy_filename fails: viewer == NULL");
        return;
    }

    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(viewer->treeview));
    if (selection == NULL)
        return;

    model = gtk_tree_view_get_model (GTK_TREE_VIEW(viewer->treeview));
    
    slist = gtk_tree_selection_get_selected_rows (selection, &model);

    if (g_list_length (slist) > 1)  /* user selected multiple columns */
        goto out;

    path = (GtkTreePath *) slist->data;

    if (gtk_tree_model_get_iter (model, &iter, path) != TRUE)
        goto out;

    gtk_tree_model_get (model, &iter, COLUMN_FILENAME, &fname, -1);

    clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);


    gtk_clipboard_set_text (clipboard, fname, -1);
    
    g_free (fname);
    
out:
    g_list_foreach (slist, (GFunc)gtk_tree_path_free, NULL);
    g_list_free (slist);
}


int
gftp_viewer_get_tree_view_position (Viewer *viewer)
{
    GtkTreePath *path = NULL;
    int pos;

    gtk_tree_view_get_cursor (GTK_TREE_VIEW (viewer->treeview), 
                              &path, NULL);
    if (path == NULL)
        pos = 0;
    else {
        pos = gtk_tree_path_get_indices (path)[0];
    }

    gtk_tree_path_free (path);
    return pos;
}

static
void do_list_files (Viewer *viewer)
{
    gftp_request *request = viewer->request;
    int ret, got;
    gftp_file *fle;

    ret = gftp_request_list_files_begin (request);
    if (ret < 0)
        return;


    /* request->gotbytes = 0; */
    free_viewer_files (viewer);


    fle = g_malloc0 (sizeof (*fle));
    while ((got = gftp_request_list_files_next (request, NULL, fle)) > 0 ||
           got == GFTP_ERETRYABLE)
    {
        if (got < 0 || strcmp (fle->file, ".") == 0) {
            gftp_file_destroy (fle, 0); /* destroy the file but not free it */
            continue;
        } else if (strcmp (fle->file, "..") == 0) {
            gftp_file_destroy (fle, 0);
            continue;
        }

        /* request->gotbytes += got; */
        viewer->files = g_list_prepend (viewer->files, fle);
    
        fle = g_malloc0 (sizeof (*fle));
    }
    
    g_free (fle);
    gftp_request_end_transfer (viewer->request);
}

static const char* get_stock(const char *filename)
{
    const char *ext;
    
    ext = strrchr (filename, '.');

    if (ext != NULL) {
        if (strcmp (ext, ".avi") == 0)
            return "video";
        if (strcmp (ext, ".mpg") == 0)
            return "video";
        if (strcmp (ext, ".mp3") == 0)
            return "sound";
        if (strcmp (ext, ".iso") == 0)
            return "iso";
        if (strcmp (ext, ".gz") == 0)
            return "package";
        if (strcmp (ext, ".tar") == 0)
            return "package";
        if (strcmp (ext, ".bz2") == 0)
            return "package";
    }

    return "gtk-file";
}

void gftp_viewer_show_files (Viewer *viewer)
{
  GList *item;
  gftp_file *fle;
  GtkListStore *store = viewer->store;
  GtkTreeIter iter;
  GtkSortType order;
  gint sort_column_id;
  int sort_column;
  gchar *tempstr;


  gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (store),
                                        &sort_column_id, &order);


  switch (sort_column_id) {
  case COLUMN_FILENAME:
    sort_column =  GFTP_SORT_COL_FILE;
    break;
  default:
    sort_column =  GFTP_SORT_COL_FILE;
    break;
  }

  viewer->files = gftp_sort_filelist (viewer->files, sort_column, 
                                      order == GTK_SORT_ASCENDING ? 1 : 0);


  g_object_ref (viewer->filter);
  gtk_tree_view_set_model (GTK_TREE_VIEW (viewer->treeview), NULL);

  gtk_list_store_clear (store);


  item = g_list_first (viewer->files);
  

  while (item != NULL) {
    fle = (gftp_file *)item->data;
 
    gtk_list_store_append (store, &iter);

    /* fprintf (stderr, "add item %s\n", fle->file); */
    if (S_ISDIR (fle->st_mode)) {
        gtk_list_store_set (store, &iter, COLUMN_STOCK, stock_dir, 
                            COLUMN_FILENAME, fle->file, 
                            COLUMN_FILEENTRY, fle, -1); 
    }
    else {
      gtk_list_store_set (store, &iter, COLUMN_STOCK, get_stock (fle->file), 
                          COLUMN_FILENAME, fle->file, 
                          COLUMN_FILEENTRY, fle, -1);
    }

    if (GFTP_IS_SPECIAL_DEVICE (fle->st_mode))
        tempstr = g_strdup_printf ("%d, %d", major (fle->size),
                                   minor (fle->size));
    else {
        // tempstr = insert_commas (fle->size, NULL, 0);
        tempstr = g_format_size_for_display (fle->size);
    }

    gtk_list_store_set (store, &iter, COLUMN_FILESIZE, tempstr, -1); 
    g_free (tempstr);


    if (fle->user)
        gtk_list_store_set (store, &iter, COLUMN_USER, fle->user, -1);

    if (fle->group)
        gtk_list_store_set (store, &iter, COLUMN_GROUP, fle->group, -1);
    
    if ((tempstr = ctime (&fle->datetime))) {
        char *pos;
        if ((pos = strchr (tempstr, '\n')) != NULL)
            *pos = '\0';
        gtk_list_store_set (store, &iter, COLUMN_DATE, tempstr, -1);
    }
    
    tempstr = gftp_convert_attributes_from_mode_t (fle->st_mode);
    gtk_list_store_set (store, &iter, COLUMN_ATTRIBS, tempstr, -1);
    g_free (tempstr);

    item = g_list_next (item);
  }

  /* column = gtk_tree_view_get_column (GTK_TREE_VIEW(viewer->treeview), 1); */
  /* gtk_tree_view_column_clicked (column); */
  /* gtk_tree_view_column_clicked (column); */

  gtk_tree_view_set_model (GTK_TREE_VIEW (viewer->treeview), viewer->filter);
  g_object_unref (viewer->filter);


  on_viewer_directory_changed (viewer);

}


static void
free_viewer_files (Viewer *viewer)
{
    if (viewer->files != NULL) {
        GList *itr;
        for (itr = g_list_first(viewer->files); itr != NULL; 
             itr = g_list_next (itr))
            g_free (itr->data);
   
        g_list_free (viewer->files);
        viewer->files = NULL;
    }
}


static void 
on_viewer_filter_entry_activate (GtkEntry *entry, Viewer *viewer)
{
    const gchar *txt;

    if (viewer->filter_str) {
        g_free (viewer->filter_str);
        g_pattern_spec_free (viewer->pattern_spec);
    }

    txt = gtk_entry_get_text (entry);
    if (txt == NULL || strlen (txt) == 0) {
        viewer->filter_str = NULL;
        viewer->pattern_spec = NULL;
    } else {
        viewer->filter_str = g_strdup (txt);
        viewer->pattern_spec = g_pattern_spec_new (txt);
    }

    refresh_viewer (viewer);
}


/* callbacks */
void on_upbtn_clicked (GtkButton *button, Viewer *viewer)
{
    char *directory;
    char *tempstr;
    int pos;

    pos = gftp_viewer_get_tree_view_position (viewer);
    gftp_viewer_add_directory_history (viewer, viewer->request->directory, pos);


    directory = gftp_build_path (viewer->request, viewer->request->directory,
                                 "..", NULL);
    tempstr = gftp_expand_path (directory);

    gftp_viewer_combo_set_text (viewer, tempstr);

    /* update_status_bar (viewer); */

    g_free (directory);
    g_free (tempstr);
}


void on_backbtn_clicked (GtkButton *button, Viewer *viewer)
{
    HistoryEntry *entry;
    GtkTreePath *path;

    if (viewer->history == NULL)
        return;

    if (viewer->his_pos == NULL) {
        /* this is the first time we navigate in history,
         * so add the current directory before we leave it. */
        int pos;
        pos = gftp_viewer_get_tree_view_position (viewer);

        gftp_viewer_add_directory_history (viewer, 
                                viewer->request->directory, pos);

        viewer->his_pos = viewer->history;
    }

    if (viewer->his_pos->next == NULL)
        return;

    viewer->his_pos = viewer->his_pos->next;
    entry = (HistoryEntry *) viewer->his_pos->data;
    
    gftp_viewer_combo_set_text (viewer, entry->path);
    path = gtk_tree_path_new_from_indices (entry->pos, -1);
    gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (viewer->treeview), path, 
                                  NULL, FALSE, 0, 0);

    /* update_status_bar (viewer); */

    gtk_tree_path_free (path);
}


void on_fwdbtn_clicked (GtkButton *button, Viewer *viewer)
{
    HistoryEntry *entry;
    GtkTreePath *path;
    
    if (viewer->his_pos == NULL)
        return;

    if (viewer->his_pos->prev == NULL)
        return;

    viewer->his_pos = viewer->his_pos->prev;
    entry = (HistoryEntry *) viewer->his_pos->data;
    
    gftp_viewer_combo_set_text (viewer, entry->path);
    path = gtk_tree_path_new_from_indices (entry->pos, -1);
    gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (viewer->treeview), path, 
                                  NULL, FALSE, 0, 0);

    /* update_status_bar (viewer); */

    gtk_tree_path_free (path);
}

/* ---------------- state related ---------------- */
void gftp_viewer_navigation_disable (Viewer *viewer)
{
    gtk_widget_set_sensitive (viewer->upbtn, FALSE);
    gtk_widget_set_sensitive (viewer->backbtn, FALSE);
    gtk_widget_set_sensitive (viewer->fwdbtn, FALSE);
    gtk_widget_set_sensitive (viewer->rfrbtn, FALSE);    
}

void gftp_viewer_navigation_enable (Viewer *viewer)
{
    on_viewer_directory_changed (viewer);
}

void on_viewer_init (Viewer *viewer)
{
    gtk_widget_set_sensitive (viewer->upbtn, FALSE);
    gtk_widget_set_sensitive (viewer->backbtn, FALSE);
    gtk_widget_set_sensitive (viewer->fwdbtn, FALSE);
    gtk_widget_set_sensitive (viewer->rfrbtn, FALSE);
    /* gtk_widget_set_sensitive (viewer->stopbtn, FALSE); */
}

void on_viewer_connecting (Viewer *viewer)
{
    gtk_widget_set_sensitive (viewer->upbtn, FALSE);
    gtk_widget_set_sensitive (viewer->backbtn, FALSE);
    gtk_widget_set_sensitive (viewer->fwdbtn, FALSE);
    gtk_widget_set_sensitive (viewer->rfrbtn, FALSE);
    /* gtk_widget_set_sensitive (viewer->stopbtn, TRUE); */
}

void on_viewer_connected (Viewer *viewer)
{
    gtk_widget_set_sensitive (viewer->upbtn, TRUE);
    gtk_widget_set_sensitive (viewer->backbtn, FALSE);
    gtk_widget_set_sensitive (viewer->fwdbtn, FALSE);
    gtk_widget_set_sensitive (viewer->rfrbtn, TRUE);
    /* gtk_widget_set_sensitive (viewer->stopbtn, FALSE); */
}


void on_viewer_directory_changed (Viewer *viewer)
{
    if (viewer->his_pos) {      /* in navigation now */
        if (viewer->his_pos == g_list_last (viewer->history))
            gtk_widget_set_sensitive (viewer->backbtn, FALSE);
        else
            gtk_widget_set_sensitive (viewer->backbtn, TRUE);

        if (viewer->his_pos != viewer->history) 
            gtk_widget_set_sensitive (viewer->fwdbtn, TRUE);
        else 
            gtk_widget_set_sensitive (viewer->fwdbtn, FALSE);
    } else {
        if (viewer->history != NULL)
            gtk_widget_set_sensitive (viewer->backbtn, TRUE);
        else
            gtk_widget_set_sensitive (viewer->backbtn, FALSE);

        gtk_widget_set_sensitive (viewer->fwdbtn, FALSE);
    }


    if (strcmp (viewer->request->directory, "/") == 0) {
        gtk_widget_set_sensitive (viewer->upbtn, FALSE);
    } else 
        gtk_widget_set_sensitive (viewer->upbtn, TRUE);

    gtk_widget_set_sensitive (viewer->rfrbtn, TRUE);
}
